package account

import (
	"context"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"webcppcluster/cache"
	"webcppcluster/config"
	"webcppcluster/db"
	"webcppcluster/utils"
)

var phonePrefixList = []int{139, 138, 137, 136, 134, 135, 147, 150, 151, 152, 157, 158, 159, 172, 178,
	130, 131, 132, 140, 145, 146, 155, 156, 166, 185, 186, 175, 176, 196,
	133, 149, 153, 177, 173, 180, 181, 189, 191, 193, 199,
	162, 165, 167, 170, 171}

// CheckPhonenum 检查手机号格式
func CheckPhonenum(phonenum string) bool {
	if len(phonenum) != 11 {
		return false
	}

	// 检查是否全为数字
	if _, err := strconv.Atoi(phonenum); err != nil {
		return false
	}

	// 检查前缀
	prefix, _ := strconv.Atoi(phonenum[:3])
	for _, p := range phonePrefixList {
		if p == prefix {
			return true
		}
	}
	return false
}

// CheckPassword 检查密码格式（8-16位，包含数字和字母）
func CheckPassword(password string) bool {
	pattern := regexp.MustCompile(`^(?=.*[0-9])(?=.*[A-z])[0-9a-zA-Z]{8,16}$`)
	return pattern.MatchString(password)
}

// CheckUserIdNotRepeat 检查用户ID是否重复
func CheckUserIdNotRepeat(userid string) (bool, error) {
	exists, err := db.CheckUserExists(userid)
	if err != nil {
		return false, err
	}
	return !exists, nil
}

// CheckIdCard 检查身份证号
func CheckIdCard(idcard string) bool {
	idcard = strings.TrimSpace(idcard)
	idcardList := []rune(idcard)

	// 地区校验
	if len(idcard) < 2 {
		return false
	}
	areaCode := idcard[:2]
	if _, ok := config.AreaID[areaCode]; !ok {
		return false
	}

	// 15位身份证
	if len(idcard) == 15 {
		year, _ := strconv.Atoi("19" + idcard[6:8])
		isLeapYear := (year%400 == 0) || (year%100 != 0 && year%4 == 0)

		var pattern *regexp.Regexp
		if isLeapYear {
			pattern = regexp.MustCompile(`[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$`)
		} else {
			pattern = regexp.MustCompile(`[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$`)
		}
		return pattern.MatchString(idcard)
	}

	// 18位身份证
	if len(idcard) == 18 {
		year, _ := strconv.Atoi(idcard[6:10])
		isLeapYear := (year%400 == 0) || (year%100 != 0 && year%4 == 0)

		var pattern *regexp.Regexp
		if isLeapYear {
			pattern = regexp.MustCompile(`[1-9][0-9]{5}(18|19|20)[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$`)
		} else {
			pattern = regexp.MustCompile(`[1-9][0-9]{5}(18|19|20)[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$`)
		}

		if !pattern.MatchString(idcard) {
			return false
		}

		// 计算校验位
		idWeight := []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
		checkCode := []interface{}{1, 0, "X", 9, 8, 7, 6, 5, 4, 3, 2}

		sum := 0
		for i := 0; i < 17; i++ {
			digit, _ := strconv.Atoi(string(idcardList[i]))
			sum += digit * idWeight[i]
		}

		lastChar := string(idcardList[17])
		if lastChar == "X" || lastChar == "x" {
			lastChar = "10"
		}
		lastDigit, _ := strconv.Atoi(lastChar)

		expectedCheck := checkCode[sum%11]
		if expectedCheckInt, ok := expectedCheck.(int); ok {
			return expectedCheckInt == lastDigit
		}
		return false
	}

	return false
}

// InitPackage 初始化背包
func InitPackage(userid int64, now time.Time, store *cache.RedisStore) error {
	strKey := fmt.Sprintf(config.KeyPackage, fmt.Sprintf("%d", userid))
	
	// 检查缓存是否存在
	if store.Exists(strKey) {
		store.Expire(strKey, config.KeyPackageExpire)
		return nil
	}

	// 从数据库获取
	items, err := db.GetPackageIdAndNum(userid)
	if err == nil && len(items) > 0 {
		// 设置缓存
		packageInfo := make(map[string]interface{})
		for _, item := range items {
			packageInfo[fmt.Sprintf("%d", item.PropID)] = item.PropNum
		}
		store.SetHash(strKey, packageInfo)
		store.Expire(strKey, config.KeyPackageExpire)
		return nil
	}

	// 创建新的背包
	packageInfo := make(map[string]interface{})
	var packageInfoList []db.PackageInfo

	for _, id := range config.ShopInitList {
		cfg, ok := config.ShopCfg[id]
		if !ok {
			continue
		}
		packageInfo[fmt.Sprintf("%d", cfg.PID)] = cfg.InitNum
		packageInfoList = append(packageInfoList, db.PackageInfo{
			PropID:   cfg.PID,
			PropNum:  cfg.InitNum,
			PropType: cfg.Type,
		})
	}

	store.SetHash(strKey, packageInfo)
	store.Expire(strKey, config.KeyPackageExpire)

	// 保存到数据库
	if len(packageInfoList) > 0 {
		return db.InitPackage(userid, packageInfoList, now)
	}
	return nil
}

// InitUser 初始化用户
func InitUser(phonenum, password, nick, sex, idcard string, store *cache.RedisStore) error {
	now := time.Now()

	// 插入用户到数据库
	if err := db.InsertRegisterUser(phonenum, password, nick, sex, idcard, now); err != nil {
		utils.LogError(err)
		return err
	}

	// 初始化背包
	userid, _ := strconv.ParseInt(phonenum, 10, 64)
	return InitPackage(userid, now, store)
}

// VerifyAccount 验证账号
func VerifyAccount(userid, password string) (int, string) {
	storedPassword, err := db.GetUserPassword(userid)
	if err != nil {
		return config.ECLoginUseridError, config.GetErrorMessage(config.ECLoginUseridError)
	}

	if storedPassword != password {
		return config.ECLoginPasswordError, config.GetErrorMessage(config.ECLoginPasswordError)
	}

	return config.ECReqNormal, ""
}

// HandleLogin 处理登录
func HandleLogin(userid string, store *cache.RedisStore) (int, string) {
	now := time.Now()

	// 设置登录缓存
	strKey := fmt.Sprintf(config.KeyLogin, userid)
	loginInfo := map[string]interface{}{
		"userid":     userid,
		"freshtime": now.Format(time.RFC3339),
	}
	store.SetHash(strKey, loginInfo)
	store.Expire(strKey, time.Duration(config.KeyLoginExpireTime)*time.Second)

	// 更新最后登录时间
	if err := db.UpdateUserLastLoginTime(userid, now); err != nil {
		utils.LogError(err)
		return config.ECReqNormal, "" // 登录成功，即使更新失败也不影响
	}

	// 初始化背包
	useridInt, _ := strconv.ParseInt(userid, 10, 64)
	InitPackage(useridInt, now, store)

	return config.ECReqNormal, ""
}

// CheckLogin 检查是否登录（用于中间件）
func CheckLogin(userid string) bool {
	return userid != ""
}

// GetPackageFromDB 从数据库获取背包数据（用于lobby包）
func GetPackageFromDB(userid int64) ([]db.PackageItem, error) {
	return db.GetPackageIdAndNum(userid)
}

